package org.fujene.memoryblock.blockwise;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;

import org.fujene.constants.Constants;
import org.fujene.diskblock.DiskBlock;
import org.fujene.memoryblock.MemoryBlock;
import org.fujene.structure.LookupPointer;
import org.fujene.toolbox.ByteConverter;

public class BlockwiseContentBlock extends MemoryBlock{
	private int opt_length = 0;
	private int opt_supplements = 0;
	private int opt_contentindex = -1;

	public BlockwiseContentBlock(int bodySize, int ID, boolean isAllocate){
		super(bodySize, ID, Constants.MEMORY_TYPE_CONTENTS, isAllocate);
	}

	private class Contents{
		long ID;
		LookupPointer[] supps;
		int strlen;
		String str;

		// 読み込み用
		public Contents(int from, boolean readStr){
			supps = new LookupPointer[opt_supplements];

			ID = ByteConverter.byteToLong(getBody(from, 8));
			from += 8;

			for(int i = 0; i < opt_supplements; i++){
				supps[i] = LookupPointer.fromBytes(getBody(from, PTR));
				from += PTR;
			}

			if (readStr){
				strlen = ByteConverter.byteToInt(getBody(from, STRLEN));
				from += STRLEN;

				try{
					str = new String(getBody(from, strlen), "UTF-8");
				}
				catch(UnsupportedEncodingException e){}
			}
		}

		// 書き込み用
		public Contents(String str, long ID){
			this.ID = ID;

			this.supps = new LookupPointer[opt_supplements];
			for(int i = 0; i < this.supps.length; i++){
				this.supps[i] = LookupPointer.EMPTY;
			}

			this.str = str;
			try{
				strlen = str.getBytes("UTF-8").length;
			}
			catch(UnsupportedEncodingException e){}
		}

		public void setSuppPtrs(LookupPointer ptr, int index){
			if (supps != null && supps.length < index) supps[index] = ptr;
		}

		// 実際に書き込み
		public void write(int from, boolean writeStr){
			setBody(ByteConverter.longToByte(this.ID, 8), from);
			from += 8;

			if (this.supps != null){
				for(int i = 0; i < this.supps.length; i++){
					setBody(this.supps[i].toBytes(), from);
					from += PTR;
				}
			}

			if (writeStr){
				setBody(ByteConverter.intToByte(strlen, STRLEN), from);
				from += STRLEN;
				try{
					setBody(this.str.getBytes("UTF-8"), from);
				}
				catch(UnsupportedEncodingException e){}
			}
		}
	}

	public String getContent(int ptr){
		Contents content = new Contents(ptr, true);

		return content.str;
	}

	public void setContent(int ptr, LookupPointer[] suppPtrs, long ID, String str){
		Contents content = new Contents(str, ID);

		content.supps = suppPtrs;

		content.write(ptr, true);

		opt_length++;
	}

	public LookupPointer[] getSuppPtr(int ptr){
		Contents content = new Contents(ptr, false);

		return content.supps;
	}

	public void setSuppPtr(int ptr, LookupPointer suppPtr, int index){
		Contents content = new Contents(ptr, false);

		content.setSuppPtrs(suppPtr, index);

		content.write(ptr, false);
	}

	public int getNewPointer(String str){
		try{
			byte[] strb = str.getBytes("UTF-8");
			int returnPtr = this.bodySize - this.bodyVacancy;
			if (this.bodyVacancy >= 8 + (opt_supplements * PTR) + STRLEN + strb.length){
				this.bodyVacancy -= 8 + (opt_supplements * PTR) + STRLEN + strb.length;
				return returnPtr;
			}else return -1;
		}
		catch(UnsupportedEncodingException e){
			return -1;
		}
	}

	public long getContentID(int ptr){
		Contents content = new Contents(ptr, false);

		return content.ID;
	}

	public int getLength(){
		return opt_length;
	}

	public int getCount(){
		return opt_length;
	}

	public static byte[] createByteArray(LookupPointer[] suppPtrs, long ID, String str) throws IOException{
		ByteArrayOutputStream stream = new ByteArrayOutputStream();

		stream.write(ByteConverter.longToByte(ID, 8));
		for(int i = 0; i < suppPtrs.length; i++){
			stream.write(suppPtrs[i].toBytes());
		}

		byte[] strbyte = str.getBytes("UTF-8");
		stream.write(ByteConverter.intToByte(strbyte.length, STRLEN));
		stream.write(strbyte);

		return stream.toByteArray();
	}

	public byte[] getData() throws IOException{
		ByteArrayOutputStream stream = new ByteArrayOutputStream(this.bodySize + 12);

		stream.write(getOptionByteStream());
		stream.write(this.getBody(0, this.bodySize));

		return stream.toByteArray();
	}

	public void setData(byte[] array){
		opt_length = ByteConverter.byteToInt(array, 0, 4);
		opt_supplements = ByteConverter.byteToInt(array, 4, 4);
		opt_contentindex = ByteConverter.byteToInt(array, 8, 4);

		setBody(array, 8, bodySize, 0);
	}

	public int getContentIndex(){
		return opt_contentindex;
	}

	@Override
	public int getOptionSize(){
		return 3;
	}

	@Override
	public byte[] getOptionByteStream(){
		ByteArrayOutputStream stream = new ByteArrayOutputStream(12);

		try{
			stream.write(ByteConverter.intToByte(opt_length, 4));
			stream.write(ByteConverter.intToByte(opt_supplements, 4));
			stream.write(ByteConverter.intToByte(opt_contentindex, 4));
		}
		catch(IOException e){}

		return stream.toByteArray();
	}

	@Override
	public int getOption(int num){
		switch(num){
		case 0:
			return opt_length;
		case 1:
			return opt_supplements;
		case 2:
			return opt_contentindex;
		default:
			return 0;
		}
	}

	@Override
	public void setOption(int option, int value){
		switch(option){
		case 0:
			opt_length = value;
			break;
		case 1:
			opt_supplements = value;
			break;
		case 2:
			opt_contentindex = value;
			break;
		}
	}

	@Override
	public String getClassName(){
		return new String("Block-wise ContentBlock");
	}

	@Override
	public DiskBlock toDisk(int from, int to, String dir){
		return null;
	}
}
